package com.cdtye.shuoh.service.device.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cdtye.common.webcore.web.AjaxJson;
import com.cdtye.shuoh.config.mqtt.MqttSubClient;
import com.cdtye.shuoh.domain.device.entity.Device;
import com.cdtye.shuoh.domain.device.entity.DeviceInfo;
import com.cdtye.shuoh.domain.device.entity.DeviceLocation;
import com.cdtye.shuoh.domain.device.entity.DeviceTopic;
import com.cdtye.shuoh.domain.device.vo.DeviceVo;
import com.cdtye.shuoh.domain.enums.DeviceConnectStatus;
import com.cdtye.shuoh.domain.enums.DeviceTopicEnum;
import com.cdtye.shuoh.domain.enums.DeviceType;
import com.cdtye.shuoh.domain.product.entity.Product;
import com.cdtye.shuoh.domain.product.entity.ProductServices;
import com.cdtye.shuoh.domain.product.vo.ProductVo;
import com.cdtye.shuoh.domain.tdengine.Fields;
import com.cdtye.shuoh.domain.tdengine.TableDto;
import com.cdtye.shuoh.mapper.device.DeviceInfoMapper;
import com.cdtye.shuoh.mapper.device.DeviceMapper;
import com.cdtye.shuoh.service.device.DeviceChannelService;
import com.cdtye.shuoh.service.device.DeviceLocationService;
import com.cdtye.shuoh.service.device.DeviceService;
import com.cdtye.shuoh.service.device.DeviceTopicService;
import com.cdtye.shuoh.service.product.ProductService;
import com.cdtye.shuoh.service.product.ProductServicesService;
import com.cdtye.shuoh.service.tgengine.TdEngineService;
import com.cdtye.shuoh.util.http.HttpRestUtils;
import com.cdtye.shuoh.util.text.TdUtils;
import org.apache.commons.io.filefilter.OrFileFilter;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class DeviceServiceImpl extends ServiceImpl<DeviceMapper, Device> implements DeviceService {

    @Autowired
    private ProductService productService;

    @Autowired
    private ProductServicesService productServicesService;

    @Autowired
    private TdEngineService tdEngineService;

    @Autowired
    private DeviceTopicService deviceTopicService;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private DeviceInfoMapper deviceInfoMapper;

    @Autowired
    private MqttSubClient mqttSubClient;

    @Autowired
    private DeviceLocationService deviceLocationService;

    @Autowired
    private DeviceChannelService deviceChannelService;




    /**
     * 数据库名称
     */
    @Value("${spring.datasource.dynamic.datasource.tdengine.dbName:cms_shuoh}")
    private String dataBaseName;

    @Value("${spring.mqtt.webUrl}")
    private String mqttAuthUrl;

    @Value("${spring.mqtt.username}")
    private String username;

    @Value("${spring.mqtt.password}")
    private String password;

    @Override
    public List<Device> selectList(DeviceVo vo){
        return deviceMapper.selectList(vo);
    }

    public List<Map<String,Object>> deviceTypeStatistic(){
        List<Map<String,Object>> result = new ArrayList<>();
        for (DeviceType deviceType : DeviceType.values()) {
            DeviceVo vo = new DeviceVo();
            vo.setDeviceType( deviceType.getValue());
            Map<String,Object> item = new HashMap<>();
            item.put("name", deviceType.getTag());
            item.put("value", selectList(vo).size());
            result.add(item);
        }
        return result;
    }

    /**
     * 新增设备管理
     * @param device
     * @return
     */
    @Override
    public AjaxJson insertDevice(Device device) throws Exception {
        Device oneByClientIdAndDeviceIdentification = deviceMapper.findOneByClientIdOrDeviceIdentification(device.getClientId(), device.getDeviceIdentification());
        if (null != oneByClientIdAndDeviceIdentification) {
            return AjaxJson.success("新增设备管理成功");
        }
        device.setConnectStatus(DeviceConnectStatus.INIT.getValue());
        //mqtt auth
       if(null!= device.getProtocolType() && "MQTT".equals(device.getProtocolType().toUpperCase())){
           JSONObject param = new JSONObject();
           param.put("user_id", device.getUserName());
           param.put("password", device.getPassword());
           HttpRestUtils.post(mqttAuthUrl,param,username,password);
       }
        int insertDeviceCount = this.baseMapper.insert(device);
        if (insertDeviceCount > 0) {
            //基础TOPIC集合
            //设备位置信息存储
            DeviceLocation deviceLocation = new DeviceLocation();
            BeanUtils.copyProperties(device.getDeviceLocation(), deviceLocation);
            deviceLocationService.insertOrUpdateByDeviceIdentification(deviceLocation);
            Map<String, String> topicMap = new HashMap<>();
            if (device.getDeviceType().equals(DeviceType.GATEWAY.getValue())) {
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/add", "边设备添加子设备");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/addResponse", "物联网平台返回的添加子设备的响应");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/delete", "边设备删除子设备");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/deleteResponse", "物联网平台返回的删除子设备的响应");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/update", "边设备更新子设备状态");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/topo/updateResponse", "物联网平台返回的更新子设备状态的响应");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/datas", "边设备上报数据");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/command", "物联网平台给设备或边设备下发命令");
                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/commandResponse", "边设备返回给物联网平台的命令响应");
            } else if (device.getDeviceType().equals(DeviceType.COMMON.getValue())) {
//                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/datas", "边设备上报数据");
//                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/command", "物联网平台给设备或边设备下发命令");
//                topicMap.put("/v1/devices/" + device.getDeviceIdentification() + "/commandResponse", "边设备返回给物联网平台的命令响应");
                Boolean commonDeviceTDSubtable = this.createCommonDeviceTDSubtable(device);
                if (!commonDeviceTDSubtable) {
                    throw new Exception("创建普通设备TD子表失败");
                }
            } else if(device.getDeviceType().equals(DeviceType.LORA_4G_GATWAY.getValue())){
                topicMap.put("/"+device.getDeviceIdentification(),"LORA转4G网关");
                DeviceTopic deviceTopic = new DeviceTopic();
                deviceTopic.setPublisher("边设备");
                deviceTopic.setTopic("/"+device.getDeviceIdentification());
                deviceTopic.setDeviceIdentification(device.getDeviceIdentification());
                deviceTopic.setSubscriber("物联网平台");
                deviceTopic.setType(DeviceTopicEnum.CUSTOM.getKey());
                deviceTopic.setRemark("LORA转4G网关");
                if(null!=deviceTopic.getTopic()){
                    mqttSubClient.subscribe(deviceTopic.getTopic());
                }
                deviceTopicService.insertDeviceTopic(deviceTopic);

            }
            //设备基础Topic数据存储
//            for (Map.Entry<String, String> entry : topicMap.entrySet()) {
//                DeviceTopic deviceTopic = new DeviceTopic();
//                deviceTopic.setDeviceIdentification(device.getDeviceIdentification());
//                deviceTopic.setType(DeviceTopicEnum.BASIS.getKey());
//                deviceTopic.setTopic(entry.getKey());
//                if (entry.getKey().startsWith("/v1/devices/") && entry.getKey().endsWith("datas")) {
//                    deviceTopic.setPublisher("边设备");
//                    deviceTopic.setSubscriber("物联网平台");
//                } else if (entry.getKey().startsWith("/v1/devices/") && entry.getKey().endsWith("commandResponse")) {
//                    deviceTopic.setPublisher("边设备");
//                    deviceTopic.setSubscriber("物联网平台");
//                } else if (entry.getKey().startsWith("/v1/devices/") && entry.getKey().endsWith("Response")) {
//                    deviceTopic.setPublisher("物联网平台");
//                    deviceTopic.setSubscriber("边设备");
//                } else {
//                    continue;
//                }
//                deviceTopic.setRemark(entry.getValue());
//                deviceTopicService.insertDeviceTopic(deviceTopic);
//            }
        }
        return AjaxJson.success("新增设备管理成功");
    }


    /**
     * 创建普通设备TD子表
     *
     * @param device
     * @return
     */
    public Boolean createCommonDeviceTDSubtable(Device device) throws Exception {
        Product product = productService.findOneByProductIdentificationAndProtocolType(device.getProductIdentification(), device.getProtocolType());
        if (null == product) {
            log.error("刷新子设备数据模型失败，子设备产品不存在");
            return false;
        }
        List<ProductServices> allByProductId = productServicesService.findAllByProductIdentificationId(product.getProductIdentification());
        TableDto tableDto;
        for (ProductServices productServices : allByProductId) {
            tableDto = new TableDto();
            tableDto.setDataBaseName(dataBaseName);
            //超级表命名规则 : 产品类型_产品标识_服务名称
            String superTableName = TdUtils.getSuperTableName(product.getProductType(), product.getProductIdentification(), productServices.getServiceName());
            tableDto.setSuperTableName(superTableName);
            //子表命名规则 : 产品类型_产品标识_服务名称_设备标识（设备唯一标识）
            tableDto.setTableName(TdUtils.getSubTableName(superTableName, device.getDeviceIdentification()));
            //Tag的处理
            List<Fields> tagsFieldValues = new ArrayList<>();
            Fields fields = new Fields();
            fields.setFieldValue(device.getDeviceIdentification());
            tagsFieldValues.add(fields);
            tableDto.setTagsFieldValues(tagsFieldValues);
            AjaxJson ctResult = tdEngineService.createTable(tableDto);
            if (200 != ctResult.getStatus()) {
                log.error("Create Table Exception: " + ctResult.getMessage());
            }
        }
        return true;
    }

    public Device getById(String id){
        Device device = deviceMapper.getById(id);
        device.setDeviceLocation(deviceLocationService.findOneByDeviceIdentification(device.getDeviceIdentification()));
        return device;
    }

    public Device findOneByDeviceIdentification(String deviceIdentification){

        return deviceMapper.findOneByDeviceIdentification(deviceIdentification);
    }

    public Device findOneByClientId(String clientId){
        return deviceMapper.findOneByClientId(clientId);
    }

    @Override
    public void updateDeviceByDeviceIdentification(Device device) {
        Device deviceByDeviceId = deviceMapper.findOneByDeviceIdentification(device.getDeviceIdentification());
        if (deviceByDeviceId == null) {
            deviceMapper.insert(device);
        }else {
            UpdateWrapper<Device> wrapper = new UpdateWrapper<>();
            wrapper.eq("DEVICE_IDENTIFICATION",device.getDeviceIdentification());
            deviceMapper.update(device,wrapper);
        }
    }

    public Device findOneByConnector(String connector){
        QueryWrapper<Device> wrapper = new QueryWrapper<>();
        wrapper.eq("CONNECTOR",connector);
        return deviceMapper.selectOne(wrapper);
    }

    /**
     * 更新设备状态通过DeviceIdentification
     * @param deviceIdentification
     * @param connectStatus
     * @return
     */
    public Boolean updateDeviceConnectStatusByDeviceIdentification(String deviceIdentification, String connectStatus){
        UpdateWrapper<Device> wrapper = new UpdateWrapper<>();
        wrapper.eq("DEVICE_IDENTIFICATION",deviceIdentification).set("CONNECT_STATUS",connectStatus);
        return this.update(wrapper);
    }

    /**
     * 更新设备状态通过cliendId
     * @param clientId
     * @param connectStatus
     * @return
     */
    public Boolean updateDeviceConnectStatusByClientId(String clientId, String connectStatus){
        UpdateWrapper<Device> wrapper = new UpdateWrapper<>();
        wrapper.eq("CLIENT_ID",clientId).set("CONNECT_STATUS",connectStatus);
        return this.update(wrapper);
    }


    /**
     * 获取某个类型的设备列表
     * @param deviceType
     * @return
     */
    public List<Device> findByDeviceType(String deviceType){
        QueryWrapper<Device> wrapper = new QueryWrapper<>();
        wrapper.eq("DEVICE_TYPE",deviceType);
        return deviceMapper.selectList(wrapper);
    }


    /**
     * 更新设备状态通过连接实体
     * @param connector
     * @param connectStatus
     * @return
     */
    public Boolean updateDeviceConnectStatusByConnector(String connector, String connectStatus){
        UpdateWrapper<Device> wrapper = new UpdateWrapper<>();
        wrapper.eq("CONNECTOR",connector).set("CONNECT_STATUS",connectStatus);
        return this.update(wrapper);
    }

    /**
     * 分页查询
     * @param vo
     * @return
     */
    public IPage<Device> pageList(DeviceVo vo){
        return this.deviceMapper.pageList(vo.page(),vo);
    }

    /**
     * 更新
     * @param vo
     * @return
     */
    public int updateDevice(DeviceVo vo){
        int count = this.deviceMapper.updateDevice(vo);
        if(count>0){
            //设备位置信息存储
            DeviceLocation deviceLocation = new DeviceLocation();
            BeanUtils.copyProperties(vo.getDeviceLocation(), deviceLocation);
            deviceLocationService.insertOrUpdateByDeviceIdentification(deviceLocation);
        }
        return count;
    }

    /**
     * 删除
     * @param id
     * @return
     */
    public int deleteById(String id){
        Device device = getById(id);
        deviceTopicService.deleteByDeviceIdentification(device.getDeviceIdentification());
        deviceLocationService.deleteByDeviceIdentification(device.getDeviceIdentification());
        deviceChannelService.cleanChannelsForDevice(device.getDeviceIdentification());
        return this.deviceMapper.deleteById(id);
    }

    public Map<String,Object> connectStatusStatistic(){
        return deviceMapper.connectStatusStatistic();
    }

    public List<Map<String,Object>> deviceStatusStatistic(){
        List<Map<String,Object>> result = new ArrayList<>();
        ProductVo vo = new ProductVo();
        vo.setProductType("0");
        List<Product> products = productService.selectList(vo);
        for (Product product : products) {
            QueryWrapper<Device> wrapper = new QueryWrapper<>();
            wrapper.eq("product_identification", product.getProductIdentification());
            int total = deviceMapper.selectCount(wrapper);
            wrapper.eq("connect_status",DeviceConnectStatus.OFFLINE.getKey());
            int offline = deviceMapper.selectCount(wrapper);

            QueryWrapper<DeviceInfo> wrapper1 = new QueryWrapper<>();
            wrapper1.eq("model",product.getModel());
            total = total + deviceInfoMapper.selectCount(wrapper1);
            wrapper1.eq("connect_status",DeviceConnectStatus.OFFLINE.getKey());
            offline = offline + deviceInfoMapper.selectCount(wrapper1);
            Map<String,Object> item = new HashMap<>();
            item.put("name",product.getProductName());
            item.put("online",total - offline);
            item.put("offline",offline);
            result.add(item);
        }
        return result;
    }
}
